/*
 * HeadFinderRules.java
 *
 * Created on August 1, 2006, 11:54 AM
 *
 */

package galronnlp.perceptron;

import galronnlp.util.Symbol;
import galronnlp.util.SymbolGenerator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;

/**
 *
 * Class containing all the standard head finding rules as described in Appendix
 * A to Michael Collins' thesis
 *
 * @author Daniel A. Galron
 */
public class HeadFinderRules {
    
    public NPHeadFinderRule NPConstituentRule;
    public HeadFinderRule ADJPConstituentRule;
    public HeadFinderRule ADVPConstituentRule;
    public HeadFinderRule CONJPConstituentRule;
    public HeadFinderRule FRAGConstituentRule;
    public HeadFinderRule INTJConstituentRule;
    public HeadFinderRule LSTConstituentRule;
    public HeadFinderRule NACConstituentRule;
    public HeadFinderRule PPConstituentRule;
    public HeadFinderRule PRNConstituentRule;
    public HeadFinderRule PRTConstituentRule;
    public HeadFinderRule QPConstituentRule;
    public HeadFinderRule RRCConstituentRule;
    public HeadFinderRule SConstituentRule;
    public HeadFinderRule SBARConstituentRule;
    public HeadFinderRule SBARQConstituentRule;
    public HeadFinderRule SINVConstituentRule;
    public HeadFinderRule SQConstituentRule;
    public HeadFinderRule UCPConstituentRule;
    public HeadFinderRule VPConstituentRule;
    public HeadFinderRule WHADJPConstituentRule;
    public HeadFinderRule WHADVPConstituentRule;
    public HeadFinderRule WHNPConstituentRule;
    public HeadFinderRule WHPPConstituentRule;
    
    private SymbolGenerator gen;
    
    public HeadFinderRules(SymbolGenerator gen) {
        this.gen = gen;
        NPConstituentRule = new NPHeadFinderRule(HeadFinderRule.RL, NP(), gen);
        ADJPConstituentRule = new HeadFinderRule(HeadFinderRule.LR, ADJP(), gen);
        ADVPConstituentRule = new HeadFinderRule(HeadFinderRule.RL, ADVP(), gen);
        CONJPConstituentRule = new HeadFinderRule(HeadFinderRule.RL, CONJP(), gen);
        FRAGConstituentRule = new HeadFinderRule(HeadFinderRule.RL, new LinkedList<String>(), gen);
        INTJConstituentRule = new HeadFinderRule(HeadFinderRule.LR, new LinkedList<String>(), gen);
        LSTConstituentRule = new HeadFinderRule(HeadFinderRule.RL, LST(), gen);
        NACConstituentRule = new HeadFinderRule(HeadFinderRule.LR, ADJP(), gen);
        PPConstituentRule = new HeadFinderRule(HeadFinderRule.RL, PP(), gen);
        PRNConstituentRule = new HeadFinderRule(HeadFinderRule.LR, new LinkedList<String>(), gen);
        PRTConstituentRule = new HeadFinderRule(HeadFinderRule.RL, PRT(), gen);
        QPConstituentRule = new HeadFinderRule(HeadFinderRule.LR, QP(), gen);
        RRCConstituentRule = new HeadFinderRule(HeadFinderRule.RL, RRC(), gen);
        SConstituentRule = new HeadFinderRule(HeadFinderRule.LR, S(), gen);
        SBARConstituentRule = new HeadFinderRule(HeadFinderRule.LR, SBAR(), gen);
        SBARQConstituentRule = new HeadFinderRule(HeadFinderRule.LR, SBARQ(), gen);
        SINVConstituentRule = new HeadFinderRule(HeadFinderRule.LR, SINV(), gen);
        SQConstituentRule = new HeadFinderRule(HeadFinderRule.LR, SQ(), gen);
        UCPConstituentRule = new HeadFinderRule(HeadFinderRule.RL, new LinkedList<String>(), gen);
        VPConstituentRule = new HeadFinderRule(HeadFinderRule.LR, VP(), gen);
        WHADJPConstituentRule = new HeadFinderRule(HeadFinderRule.LR, WHADJP(), gen);
        WHADVPConstituentRule = new HeadFinderRule(HeadFinderRule.RL, WHADVP(), gen);
        WHNPConstituentRule = new HeadFinderRule(HeadFinderRule.LR, WHNP(), gen);
        WHPPConstituentRule = new HeadFinderRule(HeadFinderRule.RL, WHPP(), gen);
    }
    
    // THE FOLLOWING METHODS INITIALIZE THE RULES
    
    private LinkedList<String> NP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("NN NNP NNPS NNS NX POS JJR");
        ret.addLast("$ ADJP PRN");
        ret.addLast("CD");
        ret.addLast("JJ JJS RB QP");
        return ret;
    }
    private LinkedList<String> ADJP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("NNS");
        ret.addLast("QP");
        ret.addLast("NN");
        ret.addLast("$");
        ret.addLast("ADVP");
        ret.addLast("JJ");
        ret.addLast("VBN");
        ret.addLast("VBG");
        ret.addLast("ADJP");
        ret.addLast("JJR");
        ret.addLast("NP");
        ret.addLast("JJS");
        ret.addLast("DT");
        ret.addLast("FW");
        ret.addLast("RBR");
        ret.addLast("RBS");
        ret.addLast("SBAR");
        ret.addLast("RB");
        return ret;
    }
    private LinkedList<String> ADVP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("RB");
        ret.addLast("RBR");
        ret.addLast("RBS");
        ret.addLast("FW");
        ret.addLast("ADVP");
        ret.addLast("TO");
        ret.addLast("CD");
        ret.addLast("JJR");
        ret.addLast("JJ");
        ret.addLast("IN");
        ret.addLast("NP");
        ret.addLast("JJS");
        ret.addLast("NN");
        return ret;
    }
    private LinkedList<String> CONJP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("CC");
        ret.addLast("RB");
        ret.addLast("IN");
        return ret;
    }
    private LinkedList<String> LST() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("LS");
        ret.addLast(":");
        return ret;
    }
    private LinkedList<String> NAC() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("NN");
        ret.addLast("NNS");
        ret.addLast("NNP");
        ret.addLast("NNPS");
        ret.addLast("NP");
        ret.addLast("NAC");
        ret.addLast("EX");
        ret.addLast("$");
        ret.addLast("CD");
        ret.addLast("QP");
        ret.addLast("PRP");
        ret.addLast("VBG");
        ret.addLast("JJ");
        ret.addLast("JJS");
        ret.addLast("JJR");
        ret.addLast("ADJP");
        ret.addLast("FW");
        return ret;
    }
    private LinkedList<String> PP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("IN");
        ret.addLast("TO");
        ret.addLast("VBG");
        ret.addLast("VBN");
        ret.addLast("RP");
        ret.addLast("FW");
        return ret;
    }
    private LinkedList<String> PRT() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("RP");
        return ret;
    }
    private LinkedList<String> QP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("$");
        ret.addLast("IN");
        ret.addLast("NNS");
        ret.addLast("NN");
        ret.addLast("JJ");
        ret.addLast("RB");
        ret.addLast("DT");
        ret.addLast("CD");
        ret.addLast("NCD");
        ret.addLast("QP");
        ret.addLast("JJR");
        ret.addLast("JJS");
        return ret;
    }
    private LinkedList<String> RRC() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("VP");
        ret.addLast("NP");
        ret.addLast("ADVP");
        ret.addLast("ADJP");
        ret.addLast("PP");
        return ret;
    }
    private LinkedList<String> S() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("TO");
        ret.addLast("IN");
        ret.addLast("VP");
        ret.addLast("S");
        ret.addLast("SBAR");
        ret.addLast("ADJP");
        ret.addLast("UCP");
        ret.addLast("NP");
        return ret;
    }
    private LinkedList<String> SBAR() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("WHNP");
        ret.addLast("WHPP");
        ret.addLast("WHADVP");
        ret.addLast("WHADJP");
        ret.addLast("IN");
        ret.addLast("DT");
        ret.addLast("S");
        ret.addLast("SQ");
        ret.addLast("SINV");
        ret.addLast("SBAR");
        ret.addLast("FRAG");
        return ret;
    }
    private LinkedList<String> SBARQ() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("SQ");
        ret.addLast("S");
        ret.addLast("SINV");
        ret.addLast("SBARQ");
        ret.addLast("FRAG");
        return ret;
    }
    private LinkedList<String> SINV() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("VBZ");
        ret.addLast("VBD");
        ret.addLast("VBP");
        ret.addLast("VB");
        ret.addLast("MD");
        ret.addLast("VP");
        ret.addLast("S");
        ret.addLast("SINV");
        ret.addLast("ADJP");
        ret.addLast("NP");
        return ret;
    }
    private LinkedList<String> SQ() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("VBZ");
        ret.addLast("VBD");
        ret.addLast("VBP");
        ret.addLast("VB");
        ret.addLast("MD");
        ret.addLast("VP");
        ret.addLast("SQ");
        return ret;
    }
    private LinkedList<String> VP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("TO");
        ret.addLast("VBD");
        ret.addLast("VBN");
        ret.addLast("MD");
        ret.addLast("VBZ");
        ret.addLast("VB");
        ret.addLast("VBG");
        ret.addLast("VBP");
        ret.addLast("VP");
        ret.addLast("ADJP");
        ret.addLast("NN");
        ret.addLast("NNS");
        ret.addLast("NP");
        return ret;
    }
    private LinkedList<String> WHADJP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("CC");
        ret.addLast("WRB");
        ret.addLast("JJ");
        ret.addLast("ADJP");
        return ret;
    }
    private LinkedList<String> WHADVP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("CC");
        ret.addLast("WRB");
        return ret;
    }
    private LinkedList<String> WHNP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("WDT");
        ret.addLast("WP");
        ret.addLast("WP$");
        ret.addLast("WHADJP");
        ret.addLast("WHPP");
        ret.addLast("WHNP");
        return ret;
    }
    private LinkedList<String> WHPP() {
        LinkedList<String> ret = new LinkedList<String>();
        ret.addLast("IN");
        ret.addLast("TO");
        ret.addLast("FW");
        return ret;
    }
    
    
    // This is a special extension of the HeadFinderRule for NPs. They are
    // treated differently, as per the thesis.
    public class NPHeadFinderRule extends HeadFinderRule {
        public NPHeadFinderRule(int dir, LinkedList<String> list, SymbolGenerator gen) {
            super(dir, list, gen);
        }
        public Node fire(Node n) {
            HashSet<Symbol> first = super.getFirst();
            if(n.children.getLast().type == Node.POS)
                return n.children.getLast().lexicalHead();
            for(int i = n.getChildren().size() - 1; i >= 0; i--) {
                Node candidate = n.getChildren().get(i);
                if(first.contains(candidate.label())) {
                    int h = n.getChildren().indexOf(candidate);
                    if(h < (n.getChildren().size() - 2) - 1 && n.getChildren().get(h + 1).label().equals(sg.create("CC")))
                        return candidate.lexicalHead();
                    else if(h > 2 && n.getChildren().get(h - 1).label().equals(sg.create("CC")))
                        return n.getChildren().get(h - 2).lexicalHead();
                    else
                        return candidate.lexicalHead();
                }
            }
            for(Iterator<Node> it = n.getChildren().iterator(); it.hasNext();) {
                Node candidate = it.next();
                if(candidate.label().equals(gen.create("NP"))) {
                    int h = n.getChildren().indexOf(candidate);
                    if(h < (n.getChildren().size() - 2) - 1 && n.getChildren().get(h + 1).label().equals(sg.create("CC")))
                        return candidate.lexicalHead();
                    else if(h > 2 && n.getChildren().get(h - 1).label().equals(sg.create("CC")))
                        return n.getChildren().get(h - 2).lexicalHead();
                    else
                        return candidate.lexicalHead();
                }
            }
            for(int i = 1; i < this.size(); i++) {
                HashSet nextH = this.get(i);
                if(super.direction == LR) {
                    for(Iterator<Node> ic = n.getChildren().iterator(); ic.hasNext();) {
                        Node candidate = ic.next();
                        if(nextH.contains(candidate.label())) {
                            int h = n.getChildren().indexOf(candidate);
                            if(h < (n.getChildren().size() - 2) - 1 && n.getChildren().get(h + 1).label().equals(sg.create("CC")))
                                return candidate.lexicalHead();
                            else if(h > 2 && n.getChildren().get(h - 1).label().equals(sg.create("CC")))
                                return n.getChildren().get(h - 2).lexicalHead();
                            else
                                return candidate.lexicalHead();
                        }
                    }
                }
                else {
                    for(int j = n.getChildren().size() - 1; j >= 0; j--) {
                        Node candidate = n.getChildren().get(j);
                        if(nextH.contains(candidate.label())) {
                            int h = n.getChildren().indexOf(candidate);
                            if(h < (n.getChildren().size() - 2) - 1 && n.getChildren().get(h + 1).label().equals(sg.create("CC")))
                                return candidate.lexicalHead();
                            else if(h > 2 && n.getChildren().get(h - 1).label().equals(sg.create("CC")))
                                return n.getChildren().get(h - 2).lexicalHead();
                            else
                                return candidate.lexicalHead();
                        }
                    }
                }
            }
            return n.getChildren().getLast().lexicalHead();
        }
    }
}
